#ifndef __SMS_PUBLISHER__
#define __SMS_PUBLISHER__


#include "sms_helper.h"
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <thread>
#include <mutex>
#include <queue>
#include <condition_variable>


namespace sms {


/*
class SentInfo
{
public:
    SentInfo(int uid_, double time_send_, double time_dt_) : uid(uid_), time_send(time_send_), time_dt(time_dt_)
    {
    }
    int uid;
    double time_send;
    double time_dt;
};
*/


class Publisher
{

public:
    Publisher(
        std::string topic_url,
        std::string topic_type,
        QoS qos=QoS::BestEffort,
        std::string ip="127.0.0.1",
        int port=9094
    );
    ~Publisher();
    void kill();
    bool publish(nlohmann::json json_msg);
    void recv_loop();
    void send_loop();
    void heartbeat_loop();
    void join();
    nlohmann::json cvimg2sms_mem(const cv::Mat& cvimg, const std::string frame_id="camera", const double timestamp=0.0);
    nlohmann::json pcl2sms_mem(const cv::Mat& pcl, const nlohmann::json fields={"x", "y", "z"}, const std::string frame_id="lidar", const double timestamp=0.0);

private:
    bool _link();
    void _heartbeat();
    void _close_socket();
    void _parse_msg(std::string msg);
    void _delay_packet_loss_rate();

    std::string _topic_url;
    std::string _topic_type;
    std::string _ip;
    int _port;
    std::mutex _send_mtx;
    std::mutex _ids_mtx;
    std::mutex _queue_mtx;
    std::queue<nlohmann::json> _send_queue;
    std::condition_variable _cv;

    double _last_send_time;
    double _last_upload_time;
    bool _force_quit;
    bool _heartbeat_running;
    bool _running;
    QoS _qos;

    std::string _last_msg;
    int _last_msg_len;
    int _upload_id;
    double _transmission_delay;

    bool _suspended;
    int _error_cnt;
    std::vector<double> _uploaded_times;
    std::vector<double> _delays;

    int _client_socket;
    struct sockaddr_in _server_addr;
    char* _buf;

    std::thread* _recv_t;
    std::thread* _send_t;
    std::thread* _heartbeat_t;

    bool _use_shm;
    // int _shm_size;
    // int _shm_fd;
    // int _shm_offset;
    // char* _shm_ptr;
    std::string _shm_name;
};


class Logger
{
public:
    Logger(
        std::string name="default",
        bool to_screen=true,
        std::string ip="127.0.0.1",
        int port=9094
    );
    ~Logger();
    void quit();
    void info(std::string msg);
    void warn(std::string msg);
    void error(std::string msg);
    void debug(std::string msg);
private:
    std::string _format_msg(std::string msg, std::string msg_type="INFO");

    std::string _name;
    bool _to_screen;
    Publisher *_pub;
};


class CSVLogger
{
public:
    CSVLogger(
        std::vector<std::string> columns,
        std::string name="default",
        std::string ip="127.0.0.1",
        int port=9094
    );
    ~CSVLogger();
    void quit();
    void append(std::vector<double> values);
private:
    std::string _name;
    std::vector<std::string> _columns;
    Publisher *_pub;
};


}

#endif
